package com.example.demo.hotload;

import com.google.gson.Gson;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * 生成.java文件---再编译为.class文件---然后loadClass---最后invoke
 */
public class ClassUtil {
 
	private static final Logger logger = LoggerFactory.getLogger(ClassUtil.class);
	private static JavaCompiler compiler;
	static{
		compiler = ToolProvider.getSystemJavaCompiler();
	}
	/**
	 * 获取java文件路径
	 * @param file
	 * @return
	 */
	private static String getFilePath(String file){
		int last1 = file.lastIndexOf('/');
		int last2 = file.lastIndexOf('\\');
		return file.substring(0, last1>last2?last1:last2)+ File.separatorChar;
	}
	/**
	 * 编译java文件
	 * @param ops 编译参数
	 * @param files 编译文件
	 */
	private static void javac(List<String> ops, String... files){
		StandardJavaFileManager manager = null;
		try{
			manager = compiler.getStandardFileManager(null, null, null);
			Iterable<? extends JavaFileObject> it = manager.getJavaFileObjects(files);
			JavaCompiler.CompilationTask task = compiler.getTask(null, manager, null, ops, null, it);
			task.call();
			if(logger.isDebugEnabled()){
				for (String file:files){
					logger.debug("Compile Java File:" + file);
				}
			}
		}
		catch(Exception e){
			logger.error(e.getMessage());
		}
		finally{
			if(manager!=null){
				try {
					manager.close();
				}
				catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	/**
	 * 生成java文件
	 * @param file 文件名
	 * @param source java代码
	 * @throws Exception
	 */
	private static void writeJavaFile(String file,String source)throws Exception{
		if(logger.isDebugEnabled()){
			logger.debug("Write Java Source Code to:"+file);
		}
		BufferedWriter bw = null;
		try{
			File dir = new File(getFilePath(file));
			if(!dir.exists()){
				dir.mkdirs();
			}
			bw = new BufferedWriter(new FileWriter(file));
			bw.write(source);
			bw.flush();
		}
		catch(Exception e){
			throw e;
		}
		finally{
			if(bw!=null){
				bw.close();
			}
		}
	}
	/**
	 * 加载类
	 * @param name 类名
	 * @return
	 */
	private static Class<?> load(String name){
		Class<?> cls = null;
		ClassLoader classLoader = null;
		try{
			classLoader = ClassUtil.class.getClassLoader();
			cls = classLoader.loadClass(name);
			if(logger.isDebugEnabled()){
				logger.debug("Load Class["+name+"] by "+classLoader);
			}
		}
		catch(Exception e){
			logger.error(e.getMessage());
		}
		return cls;
	}
	/**
	 * 编译代码并加载类
	 * @param filePath java代码路径
	 * @param source java代码
	 * @param clsName 类名
	 * @param ops 编译参数
	 * @return
	 */
	public static Class<?> loadClass(String filePath,String source,String clsName,List<String> ops){
		try {
			logger.info("filePath:"+filePath);
			writeJavaFile( filePath,source);
			javac(ops,filePath);
			return load(clsName);
		}
		catch (Exception e) {
			logger.error(e.getMessage());
		}
		return null;
	}
	/**
	 * 调用类方法
	 * @param cls 类
	 * @param methodName 方法名
	 * @param paramsCls 方法参数类型
	 * @param params 方法参数
	 * @return
	 */
	public static Object invoke(Class<?> cls,String methodName,Class<?>[] paramsCls,Object[] params){
		Object result = null;
		try {
			Method method = cls.getDeclaredMethod(methodName, paramsCls);
			Object obj = cls.newInstance();
			result = method.invoke(obj, params);
		}
		catch (Exception e) {
			logger.error(e.getMessage());
		}
		return result;
	}
 
 
	/**
	 * 执行JS函数，参数和返回值都是String类型
	 * @param javaStr 脚本
	 * @param className 类名
	 * @param methodName 方法名
	 * @param parameter 参数
	 * @return
	 */
	public Object convertByJava(String javaStr, String className,String methodName, Object parameter) {
		StringBuilder sb = new StringBuilder();
		sb.append(javaStr);
		//设置编译参数
		ArrayList<String> ops = new ArrayList<String>();
		ops.add("-Xlint:unchecked");
		//编译代码，返回class
		Class<?> cls = ClassUtil.loadClass(System.getProperty("user.dir")+"\\target\\classes\\"+className+".java", sb.toString(), className, ops);
		//执行方法
		Object result = ClassUtil.invoke(cls, methodName, new Class[]{List.class}, new Object[]{parameter});
		//输出结果
		logger.info("parameter:" + new Gson().toJson(parameter));
		logger.info("result:" + result);
		return result;
	}
 
}